共计 5450 个字符,预计需要花费 14 分钟才能阅读完成。
Python 中,一切皆对象。每个对象由:标识(identity)、类型(type)、值(value)组成。
id(obj)返回对象 obj 的标识;type(obj)返回对象 obj 的类型;print(obj)直接打印出对象 obj 的值。
同一运算符:is
用来比较 id,==
用来比较值(本质是调用__eq__())。is 运算符比 == 效率高,在变量和 None 进行比较时,应使用 is。
特殊成员
在 Python 类中存在一些特殊方法(一般称之为魔术方法),这些方法都是 __方法__
格式,这种方法(内置方法)在内部均有特殊含义。
__init__初始化方法
专门用来定义一个类具有哪些属性的方法。
__del__方法
对象被从内存中销毁前,会被自动调用。
__new__构造方法
很少用,但比较重要。使用类名 () 创建对象时,Python 解释器首先调用__new__方法为对象分配空间。
__new__是由 object 基类提供的内置静态方法,主要作用有 2 个:
- 在内存中为对象分配空间
- 返回对象的引用
class Foo(object):
def __init__(self, name):
print("第二步:初始化对象,在空对象中创建数据")
self.name = name
def __new__(cls, *args, **kwargs):
print("第一步:先创建空对象并返回")
return object.__new__(cls)
obj = Foo("李小龙")
__call__方法
class Foo(object):
def __call__(self, *args, **kwargs):
print("执行 call 方法")
obj = Foo()
obj() # 对象 +()执行 call 方法
__str__方法
当使用 print 输出对象的时候,如果定义了__str__方法,那么就会打印从这个方法中 return 的数据。该方法需要返回一个字符串。
class Foo(object):
def __init__(self, name):
self.name = name
def __str__(self):
return "我是{}".format(self.name)
obj = Foo('李小龙')
print(obj) # 结果为:我是李小龙
__repr__这个特殊方法会在对当前对象使用 repr()函数时调用,它的作用是指定对象在 ’ 交互模式 ’ 中直接输出的效果。
__dict__方法
class Foo(object):
def __init__(self, name, age):
self.name = name
self.age = age
obj = Foo('李小龙', 18)
print(obj.__dict__)
用类名调用__dict__,会输出该类中所有类属性组成的字典;而使用类的实例对象调用__dict__,会输出所有实例属性组成的字典。
__getitem__方法、__setitem__方法、__delitem__方法
class Foo(object):
def __getitem__(self, item):
print(item)
def __setitem__(self, key, value):
print(key, value)
def __delitem__(self, key):
print('删除')
obj = Foo()
obj["x1"] # 自动触发类中__getitem__
obj['x2'] = 123 # 自动触发类中__setitem__
del obj['x3'] # 自动触发类中__delitem__
__enter__和__exit__方法
class Foo(object):
def __enter__(self):
print("进来了")
return 666
def __exit__(self, exc_type, exc_val, exc_tb):
print("出去了")
obj = Foo()
# with 对象 as f:在内部会执行__enter__方法,这时 f 是 666
# 当 with 缩进中的代码执行完毕,会自动执行__exit__方法
with obj as f:
print(123)
print(f)
输出结果如下:
进来了
123
666
出去了
__add__等
对象 + 值,内部会去执行 对象.__add__
方法,并将 + 后面的值当做参数传递过去。
class Foo(object):
def __init__(self, age):
self.age = age
def __add__(self, other):
return self.age + other.age
obj1 = Foo(18)
obj2 = Foo(20)
obj = obj1 + obj2
print(obj) # 结果为:38
__iter__方法
迭代器
迭代器类型的定义:
- 类中定义 iter 和 next 这两个方法
- iter 方法需要返回对象本身,即:self
- next 方法返回下一个数据,如果没有数据了,则需要抛出一个 StopIteration 的异常
class IT(object): # 创建迭代器类型
def __init__(self):
self.counter = 0
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter == 3:
raise StopIteration()
return self.counter
# 根据类实例化一个迭代器对象:obj1 = IT()
v1 = next(obj1) # obj1.__next__()
print(v1) # 结果为:1
v2 = next(obj1)
print(v2) # 结果为:2
# v3 = next(obj1) # 抛出异常
# print(v3)
obj2 = IT()
for item in obj2:
print(item)
for 循环内部在循环时,先执行__iter__方法,获取一个迭代器对象,然后不断执行的__next__取值(有异常 StopIteration 则终止循环)。
生成器
# 创建生成器函数
def func():
yield 1
yield 2
# 创建生成器对象(内部是根据生成器类 generator 创建的对象),生成器类的内部也声明了__iter__、__next__方法
obj = func()
v1 = next(obj)
print(v1) # 结果为:1
v2 = next(obj)
print(v2) # 结果为:2
## v3 = next(obj) # StopIteration
## print(v3)
for item in obj:
print(item)
如果按照迭代器的规定来看,其实生成器类也是一种特殊的迭代器类(生成器也是一种特殊的迭代器)。
可迭代对象
如果一个类中有__iter__方法且返回一个迭代器对象,则称这个类创建的对象为可迭代对象。
class IT(object): # 创建迭代器类型
def __init__(self):
self.counter = 0
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter == 3:
raise StopIteration()
return self.counter
class Foo(object):
def __iter__(self):
return IT()
obj = Foo() # obj 是可迭代对象
for item in obj:
print(item)
继承
mro 和 c3 算法
如果类中存在继承关系,可以通过 mro()
获取当前类的继承关系。
class A(object):
pass
class B(object):
pass
class C(A, B):
pass
print(C.mro())
print(C.__mro__)
输出结果如下:
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
如果用正经的 C3 算法规则去分析一个类继承关系有点繁琐,尤其是遇到一个复杂的类要分析很久。总结:从左到右,深度优先,大小钻石,留住顶端,基于这句话可以更快的找到继承关系。
类方法
类属性是针对类对象定义的属性,类方法是针对类对象定义的方法。语法如下:
class Tool():
count = 0 # 类属性
@classmethod # 类方法,用修饰器 @classmethod 来标识
def show_count(cls):
print(f'工具对象的数量为 {cls.count}')
def __init__(self, name):
self.name = name
Tool.count += 1
## 创建工具对象
tool = Tool('斧头')
tool2 = Tool('锯子')
# 调用类方法
Tool.show_count()
静态方法
如果类中封装的一个方法:
- 既不需要访问实例属性或者调用实例方法
- 也不需要访问类属性或者调用类方法
这个时候,就可以把这个方法封装成一个静态方法。静态方法既不需要传递类对象也不需要传递实例对象,有利于减少不必要的内存占用和性能消耗。
静态方法用修饰器 @staticmethod 来标识:
class Tool():
@staticmethod
def show():
print('工具对象')
def __init__(self, name):
self.name = name
# 创建工具对象
tool = Tool('斧头')
# 调用类方法
Tool.show()
单例
让类创建的对象,在系统中只有唯一的一个实例,即单例。实现步骤为:
- 定义一个类属性,初始值为 None,用于记录单例对象的引用
- 重写__new__方法
- 如果类属性 is None,调用父类方法分配空间,并在类属性中记录结果
- 返回类属性中记录的对象引用
class Tool():
instance = None
def __new__(cls, *args, **kwargs):
if cls.instance is None:
cls.instance = super().__new__(cls)
return cls.instance
tool = Tool()
tool2 = Tool()
print(tool)
print(tool2)
只执行一次初始化
每次使用类名 () 创建对象时,Python 解释器会自动调用两个方法:__new__、__init__。要让初始化动作只被执行一次,实现步骤为:
- 定义一个类属性 init_flag,标记是否执行过初始化动作,初始值为 False
- 在__init__方法中,判断 init_flag,如果为 False 就执行初始化动作
- 然后将 init_flag 设置为 True
class Tool():
instance = None
init_flag = False
def __new__(cls, *args, **kwargs):
if cls.instance is None:
cls.instance = super().__new__(cls)
return cls.instance
def __init__(self):
if Tool.init_flag:
return
print('初始化')
Tool.init_flag = True
tool = Tool()
tool2 = Tool()
print(tool)
print(tool2)
装饰器
Python 内置的 @property 装饰器负责把一个方法变成属性调用。
class Square:
def __init__(self, w, h):
self.__height = h
self.__width = w
def set_side(self, new_side):
self.__height = new_side
self.__width = new_side
@property
def height(self):
return self.__height
@height.setter
def height(self, new_value):
if new_value >= 0:
self.__height = new_value
else:
raise Exception("Value must be larger than 0")
s = Square(2, 2)
print(s.height) # 2
s.height = 5
print(s.height) # 5
把一个 getter 方法变成属性,只需要加上 @property 就可以了。此时,@property 本身又创建了另一个装饰器 @height.setter,负责把一个 setter 方法变成属性赋值。
只定义 getter 方法,不定义 setter 方法就是一个只读属性。
类属性方式
class A:
def __init__(self):
self.__age = 0
def get_age(self):
return self.__age
def set_age(self, age):
if age < 0 or age > 120:
raise ValueError("Invalid age")
self.__age = age
# 类属性方式的 property 属性
age = property(get_age, set_age)
a = A()
print(a.age) # 0
a.age = 25
print(a.age) # 25
多态
多态是指同一个方法调用由于对象不同可能会产生不同的行为。关于多态要注意以下 2 点:
- 多态是方法的多态,属性没有多态
- 多态的存在有 2 个必要条件:继承、方法重写